﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Text.RegularExpressions;

namespace AsyncTcpClient
{
    public partial class FormClient : Form
    {
        private bool isExit = false;
        //用于线程间互操作
        private delegate void SetListBoxCallback(string str);
        private SetListBoxCallback setListBoxCallback;
        private delegate void SetRichTextBoxReceiveCallback(string str);
        private SetRichTextBoxReceiveCallback setRichTextBoxReceiveCallback;
        private TcpClient client;
        private NetworkStream networkStream;
        //用于线程同步，初始状态设为非终止状态，使用手动重置方式
        EventWaitHandle allDone = new EventWaitHandle(false, EventResetMode.ManualReset);
        public FormClient()
        {
            InitializeComponent();
            listBoxStatus.HorizontalScrollbar = true;
            setListBoxCallback = new SetListBoxCallback(SetListBox);
            setRichTextBoxReceiveCallback =
                new SetRichTextBoxReceiveCallback(SetRichTextBoxReceive);
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            //使用IPV4
            client = new TcpClient();//AddressFamily.InterNetwork);
            //实际使用时要将Dns.GetHostName()变为服务器域名或IP地址
            IPAddress[] serverIp = Dns.GetHostAddresses(Dns.GetHostName());

            IPAddress address = null;
            foreach (var item in serverIp)
            {
                bool isMatch = Regex.IsMatch(item.ToString(), @"^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$");
                if (isMatch)
                {
                    address = item;
                    break;
                }
            }

            //创建一个委托，让其引用在异步操作完成时调用的回调方法
            AsyncCallback requestCallback = new AsyncCallback(RequestCallback);
            //将事件的状态设置为非终止状态
            allDone.Reset();
            client.BeginConnect(address, 51888, requestCallback, client);
                
            listBoxStatus.Invoke(setListBoxCallback, string.Format("本机EndPoint：{0}",
              client.Client.LocalEndPoint));
            listBoxStatus.Invoke(setListBoxCallback, "开始与服务器建立连接");
            //阻塞当前线程，即窗体界面不再响应任何用户操作，等待BeginConnect完成
            //这样做的目的是为了与服务器连接有结果（成功或失败）时，才能继续
            //当BeginConnect完成时，会自动调用RequestCallback，
            //通过在RequestCallback中调用Set方法解除阻塞
            allDone.WaitOne();
        }

        private void RequestCallback(IAsyncResult ar) 
        {
            //异步操作能执行到此处，说明调用BeginConnect已经完成，
            //并得到了IAsyncResult类型的状态参数ar，但BeginConnect尚未结束
            //此时需要解除阻塞,以便能调用EndConnect
            allDone.Set();
            //调用Set后，事件状态变为终止状态，当前线程继续，
            //buttonConnect_Click执行结束，
            //同时窗体界面可以响应用户操作
            try 
            {
                //获取连接成功的得到的状态参数
                client = ar.AsyncState as TcpClient;
                //异步接受传入的连接尝试，使BeginConnect正常结束
                client.EndConnect(ar);
                listBoxStatus.Invoke(setListBoxCallback,
                  string.Format("与服务器{0}连接成功", client.Client.RemoteEndPoint));
                //获取接收和发送数据的网络流
                networkStream = client.GetStream();
                //异步接收服务器发送的数据，BeginRead完成后，会自动调用ReadCallback
                ReadObject readObject =
                   new ReadObject(networkStream, client.ReceiveBufferSize);
                networkStream.BeginRead(readObject.bytes,
                   0, readObject.bytes.Length, ReadCallback, readObject);
            }
            catch (Exception err)
            {
                listBoxStatus.Invoke(setListBoxCallback, err.Message);
                return;
            }
        }

        private void ReadCallback(IAsyncResult ar) 
        {
            //异步操作能执行到此处，说明调用BeginRead已经完成
            try
            {
                ReadObject readObject = ar.AsyncState as ReadObject;
                int count = readObject.netStream.EndRead(ar);
                richTextBoxReceive.Invoke(setRichTextBoxReceiveCallback,
                    System.Text.Encoding.UTF8.GetString(readObject.bytes, 0, count));
                if (isExit == false) 
                {
                    //重新调用BeginRead进行异步读取
                    readObject = new ReadObject(networkStream, client.ReceiveBufferSize);
                    networkStream.BeginRead(readObject.bytes,
                 0, readObject.bytes.Length, ReadCallback, readObject);
                }
            }
            catch (Exception err) 
            {
                listBoxStatus.Invoke(setListBoxCallback, err.Message);
            }
        }

        private void SendString(string str) 
        {
            try
            {
                byte[] bytesData = System.Text.Encoding.UTF8.GetBytes(str + "\r\n");
                networkStream.BeginWrite(bytesData, 0, bytesData.Length,
                    new AsyncCallback(SendCallback), networkStream);
                
            }
            catch (Exception err) 
            {
                listBoxStatus.Invoke(setListBoxCallback, err.Message);
            }
        }

        private void SendCallback(IAsyncResult ar) 
        {
            try 
            {
                NetworkStream networkStream = ar.AsyncState as NetworkStream;
                networkStream.EndWrite(ar);
                networkStream.Flush();
            }
            catch (Exception err)
            {
                listBoxStatus.Invoke(setListBoxCallback, err.Message);
            }
        }

        private void SetListBox(string str) 
        {
            listBoxStatus.Items.Add(str);
            listBoxStatus.SelectedIndex = listBoxStatus.Items.Count - 1;
            listBoxStatus.ClearSelected();
        }

        private void SetRichTextBoxReceive(string str) 
        {
            richTextBoxReceive.AppendText(str);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            SendString(richTextSend.Text);
            richTextSend.Clear();
        }

        private void FormClient_FormClosing(object sender, FormClosingEventArgs e)
        {
            isExit = true;
            allDone.Set();
        }


    }
}
